const path = require("path");
const fs = require("fs");
const parser = require("@babel/parser");
const traverse = require("@babel/traverse").default;
const template = require("@babel/template").default;
const generate = require("@babel/generator").default;

class Complilation {

  white_list = ["",".js"]; //默认只能处理.js,不带后缀默认为js文件 

  constructor(options,context){
    this.options = options;
    this.outPutDir = this.options.output.path; // 输出目录
    this.entry = this.options.entry; // 入口文件
    this.dirname = path.dirname(this.options.entry); // 入口文件的父文件夹路径
    this.distpath = path.join(this.outPutDir,this.options.output.filename); // 打包生成的文件名
    this.compiler = context;
    this.ast = null; //当前正在编译的ast树
    this.modules = {};
    this.assets = {};
  }


  /**
 * 后缀名处理
 */
 extHandler(pathname){
  const white_list = [...this.white_list,".json"];
  for(let i =0;i<white_list.length;i++){
     const filename = pathname + white_list[i];
     if(fs.existsSync(filename)){
        return filename;
     } 
  }
}
  
  //依赖分析
  analyseLib(code,filename,relative_path){

    const ext = path.extname(filename); // 获取文件后缀

    if(this.white_list.includes(ext)){ // 只有js文件需要解析依赖
      
      const ast = parser.parse(code,{
        sourceType:"module"
      })

      this.cur_task = { //存正在编译的ast树
        ast,
        filename,
        js_path:relative_path // js的相对路径
      }
      
      const deps = [];
  
      traverse(ast, {
        CallExpression(path) {
          if (path.node.callee.type === "Identifier" && path.node.callee.name === "require") {
             deps.push(path.node.arguments[0].value);
          }
        },
      });

      this.queue(deps);
  
      this.modules[relative_path?relative_path:filename] = {
        code,
        deps
      }

      for(let i = 0; i < deps.length ; i++){
        const dep = deps[i]; 
        this.moduleResolve(this.extHandler(path.join(this.dirname,dep)),dep)
      }  

    }else{ // 非js文件需要替换require语句

      const { js_path,filename,ast } = this.cur_task;

      traverse(ast, {
        CallExpression(path) {
          if (path.node.callee.type === "Identifier" && path.node.callee.name === "require" && path.node.arguments[0].value === relative_path) {
            const dist_ast = template.ast`
              ${code}
            `;
             path.replaceWith(dist_ast);
          }
        },
      });

      const result = generate(ast);

      if(result){

        this.modules[js_path?js_path:filename].code = result.code;
      }

    }
    
  }

  //将所有解析css,图片的任务放前面,解析js的任务放最后.
  queue(deps){
      let index = 0;
      for(let i = 0;i< deps.length; i++){
         const ext = path.extname(deps[i]);
         if(!this.white_list.includes(ext)){ // 非js文件要提到前面去
            const del = deps.splice(i,1)[0];
            deps.splice(index,0,del);
            index++;
         }
      }
  }

  bundleResolve(){
    const code = `
        ;(function(entry,modules){
          function require(pathname){
            var module = {
                exports:{}
            }
            ;(function(require,module,exports){
              const code = modules[pathname].code;
              try{
                eval(code);
              }catch(error){
                  console.log(error);
              }
            })(require,module,module.exports);
            return module.exports;
          }
          require(entry);
        })(${JSON.stringify(this.entry)},${JSON.stringify(this.modules)});
    `;
    this.assets[this.options.output.filename] = code;
  }
  
  // 开始执行代码构建
  buildModule(){
    this.moduleResolve(this.entry);
    this.bundleResolve();
    return this.assets; 
  } 

  /**
   * 处理模块资源
   */
  moduleResolve(filename,relative_path){
      
    const ext = path.extname(filename); // 获取文件后缀
    
    const rule = this.options.module.rules.find((item)=>{
      return item.test.test(ext); // 寻找到与文件后缀名匹配上的loader
    })

    let code = fs.readFileSync(filename);

    if(rule){

       const fn_list = [...rule.use];

       code = this.loaderResolve(fn_list,code,relative_path);      
      
    }

    this.analyseLib(code,filename,relative_path);
    
  }

  loaderResolve(fn_list,code,relative_path){

    const raw = code; // 二进制代码

    code = code.toString();

    const loader = fn_list.pop();

    if(loader){

      let result;

      if(typeof loader === "function"){ // 函数类型

        result = loader.call({filename:relative_path,context:this.compiler,raw},code);

      }else if(typeof loader === "object"){ // 对象类型

        const { loader:loaderFn,options } = loader;
        
        result = loaderFn.call({query:options,filename:relative_path,context:this.compiler,raw},code);

      }

      code = this.loaderResolve(fn_list,result);

    }

    return code;
  }

}

module.exports = Complilation;